	#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <mutex>
#include <string>
using namespace std;
namespace lnb
{

template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~auto_ptr()
		{
			delete _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		auto_ptr(auto_ptr& apr)
		{
			_ptr = apr._ptr;
			apr._ptr = nullptr;
		
		}

		auto_ptr& operator=(auto_ptr& apr)
		{
			_ptr = apr._ptr;
			apr._ptr = nullptr;
			return *this;
		}
	private:
		T* _ptr;
	};

template<class T>
class shared_ptr
{
public:
	shared_ptr(T* ptr=nullptr)
		:_ptr(ptr)
	{
		_cnt = new int(1);
		_mtx = new mutex;
	}

	shared_ptr(T* ptr,function<void(T*)> func)
		:_ptr(ptr)
		,del(func)
	{
		_cnt = new int(1);
		_mtx = new mutex;
	}


	~shared_ptr()
	{
		Release();
		
	}

	void Release()
	{
		bool flag = false;
		_mtx->lock();
		if (--(*_cnt) == 0)
		{
			delete _cnt;
			del(_ptr);
			flag = true;
		}
		_mtx->unlock();
		if (flag)
		{
			delete _mtx;
		}
	}

	T* get()const
	{
		return _ptr;
	}

	int use_count()const
	{
		return *_cnt;
	}

	void AddCount()
	{
		_mtx->lock();
		(*_cnt)++;
		_mtx->unlock();
	}



	T& operator*()
	{
		if (_ptr == nullptr)
		{
			throw "nullptr";
		}
		return *_ptr;
	}

	T* operator->()
	{
		if (_ptr == nullptr)
		{
			throw "nullptr";
		}

		return _ptr;
	}

	shared_ptr& operator=(shared_ptr<T>& spr)
	{
		if (this->_ptr !=spr._ptr)
		{
			Release();
			_cnt = spr._cnt;
			_ptr = spr._ptr;
			_mtx = spr._mtx;
		}
		AddCount();
		return *this;

	}
	 shared_ptr(shared_ptr<T>& spr)
	{
		_cnt = spr._cnt;
		_ptr = spr._ptr;
		_mtx = spr._mtx;
		AddCount();

		

	}


private:
	int* _cnt;
	T* _ptr;
	mutex* _mtx;
	function<void(T*)> del = [](T* ptr)->void {delete ptr; };
};

template<class T>
class weak_ptr
{
public:
	weak_ptr(T* ptr=nullptr)
		:_ptr(ptr)
	{}

	weak_ptr<T>& operator=(lnb::shared_ptr<T>& spr)
	{
		_ptr = spr.get();
		return *this;
	}

	weak_ptr(lnb::shared_ptr<T>& spr)
	{
		_ptr = spr.get();
	}


	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;


};






}